package com.ec.edu.server.hello;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ec.edu.common.config.ApplicationContextProvider;
import com.ec.edu.common.constants.NettyConstant;
import com.ec.edu.common.enums.MsgType;
import com.ec.edu.common.constants.NettyMap;
import com.ec.edu.common.enums.RoomType;
import com.ec.edu.common.vo.*;
import com.ec.edu.server.hello.handler.RoomTypeHandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.UUID;

/**
 * 客户端向服务端发起送数据，先把数据放在缓冲区，服务器端再从缓冲区读取，类似于[ 入栈, 入境 ]
 */
@Slf4j
@Component
public class MsgPackServerHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel注册");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel未注册");
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel活跃状态");
        Channel incoming = ctx.channel();
        System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"在线");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端与服务端断开连接之后");
        Channel incoming = ctx.channel();
        System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"掉线");
        //1、断线重连
        //断线时，触发channelInactive方法，然后调用run()方法实现重连；
        //2、client启动连接服务器时，连接失败，调用run()方法实现重连；
        //run()：重连，如果没有连上服务端，则触发channelInactive方法，再次循环调用run()；如果连接上，则触发channelActive方法，把clientId和socketChannel存储起来

        super.channelInactive(ctx);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel读取数据完毕");
        ctx.flush();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("用户事件触发:" + evt);
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel可写事件更改");
        super.channelWritabilityChanged(ctx);
    }

    @Override
    //channel发生异常，若不关闭，随着异常channel的逐渐增多，性能也就随之下降
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("捕获channel异常");
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("助手类添加");
        NettyMap.channels.add(ctx.channel());
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("助手类移除");
        NettyMap.channels.remove(ctx.channel());
        super.handlerRemoved(ctx);
    }

    /**
     *
     * @param ctx 上下文对象
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        //获取当前channel
        Channel currentChannel = ctx.channel();
        System.out.println(currentChannel.remoteAddress());

        log.info("收到客户端消息:{}", JSON.toJSONString(msg));

        //msg是对象
        String jsonStr = JSONObject.toJSONString(msg);
        MessageVo messageVo = JSON.parseObject(jsonStr, new TypeReference<MessageVo>(){});

        //1.通过长下文对象，把响应刷到客户端
        messageVo.setRemark("服务端已收到，并回复：Hello Netty");

        //TODO save to mongodb
        if (!StringUtils.isEmpty(messageVo.getClientId()) || !StringUtils.isEmpty(messageVo.getToClientId())) {
            //把聊天记录保存到数据库，同时标记消息的签收状态[未签收]
            messageVo.setMessageId(UUID.randomUUID().toString());
        }

        //相应给客户端
        ctx.writeAndFlush(messageVo);

        //2.处理消息
        this.producerRunRoomType(ctx, messageVo, jsonStr);


    }

    /**
     * 处理消息
     * @param ctx
     * @param messageVo
     * @param jsonStr
     */
    private void producerRunRoomType(ChannelHandlerContext ctx, MessageVo messageVo, String jsonStr) {
        MsgType msgType = MsgType.getFromMsgType(messageVo.getMsgType());
        RoomType roomType = RoomType.getFromRoomType(messageVo.getRoomType());

        RoomTypeHandler roomTypeHandler = ApplicationContextProvider.getBean(roomType.getServiceName(), RoomTypeHandler.class);
        roomTypeHandler.dealRoom(ctx, msgType, roomType, jsonStr, NettyConstant.MsgPac_Code);
    }

}
